//
//  ChatViewModel.swift
//  imprint
//
//  Created by 小柠檬🍋 on 2025/5/1.
//

import Foundation
import SwiftUI

class ChatViewModel : BaseViewModel {
    @Published var aiRole: AiRoleBean? = nil
    @Published var items = [any ChatBaseView]()
    @Published var aiItems = [AiRoleBean]()
    @Published var status = ""
    @Published var scrollBottom = false
    @Published var scrollBottom2 = false //有动画的滚动
    @Published var scrollChat = "" //滚动到指定的chat
    @Published var allowMore = false //允许下拉加载更多
    @Published var loadTopId = "" //加载后的最后一个id
    @Published var voiceStatus = 0
    @Published var aiRoleId : String? = nil
    @Published var isNoMore = false//向上加载
    @Published var isNoMoreDown = false//向下加载
    @Published var targetChatBean: ChatBean? = nil//引用
    @Published var bgUrl:String? = nil //背景
    @Published var choose = false //多选模式
    var refreshId :String = ""//刷新id

    private let diffMinuteValue = 5
    
    override init() {
        super.init()
        self.pageSize = 300 //由于加载体验过差，所以先用数量大加载
        self.addObserve(self, .addChat) { (selfInstance, bean: ChatBean) in
            //添加数据
            if bean.aiUserId == selfInstance.aiRole?.objectId {
                selfInstance.items.append(bean)
                bean.read = true
                selfInstance.saveChat(bean: bean,isToast: false)
                selfInstance.scrollBottom2 = true
            }
        }
        self.addObserve(self, .emojiPicture) { (selfInstance, bean: EmojiPictureBean) in
            //添加数据
            selfInstance.doAddChatPicture(emojiBean: bean,lastChatBean : selfInstance.getLast())
        }
        self.addObserve(self, .updateAiRole) { (selfInstance, update: Bool) in
            selfInstance.doReset()
        }
        self.addObserve(self, .updateBubble) { (selfInstance, update: Bool) in
            selfInstance.doReset()
        }
        self.addObserve(self, .searchChat) { (selfInstance, id: String) in
            //搜索数据
            selfInstance.scrollChat = id
        }
    }
    
    func doReset() {
        allowMore = false //需要取消下拉加载
        self.getData(false,true)
    }
    
    func getData(_ isPull: Bool = false,_ isRefresh: Bool = false) {
        showLog("开始加载\(isPull),,\(isRefresh)")
        self.allowMore = false //向上加载
        
        requestList(isRefresh: isRefresh) { [self] in
            if isPull {
                //延迟主要是避免滚动还没有结束，但是马上插入数据了，导致滚动到指定位置实效问题
                try await Task.delay(1000)
            }
            //ai角色
            //有数据就显示有数据，没有数据就随机数据
            if isRefresh && self.aiRole == nil {
                let aiRoleBean = try await BackupsManager.shared.getDelegateConfig()?.getAiRole(roleId: self.aiRoleId)
                await MainActor.run {
                    self.aiRole = aiRoleBean
                    self.aiRoleId = aiRoleBean?.objectId
                }
            }
            var timestamp :Int64
            let curTime = getMillisecond(getNetTimeLocal())
            if (isPull) {
                //刷新取第一个
                timestamp = self.items.first?.time ?? curTime
            } else if (isRefresh) {
                //非isPull，并且是刷新，则取当前时间戳
                timestamp = curTime
            }  else {
                //加载取最后一个
                timestamp = self.items.last?.time ?? curTime
            }
            let listAll = try await getData(isRefresh, timestamp: timestamp)
            //            try check(!isRefresh || !list.isEmpty, "暂无聊天记录")
            return listAll
        } suc: { chats in
            self.bgUrl = self.aiRole?.chatBg ?? ""
            if(isPull == false && isRefresh == true){
                //更新下刷新id，避免试图没有更新
                self.refreshId = UUID().uuidString
            }
            self.status = ""
            if isRefresh {
                if isPull {
                    self.items.insert(contentsOf: chats, at: 0)
                    self.isNoMore = self.noMore
                } else {
                    self.isNoMore = self.noMore
                    self.isNoMoreDown = self.noMore
                    self.items = chats
                    self.scrollBottom = true
                }
            } else {
                //加载
                self.items.append(contentsOf: chats)
                self.isNoMoreDown = self.noMore
            }
            //写到这里todo
            if self.items.count > chats.count {
                //count 不能-1， 因为我们是加载后的上一个数据
                self.loadTopId = self.items[chats.count].objectId ?? ""
            }
            //需要延迟2s
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                self.allowMore = !self.isNoMore
            }
        } fail: {
            self.status = $0.message
        }
    }
    
    private func getData(_ isRefresh: Bool = false,timestamp:Int64) async throws-> [any ChatBaseView] {
        var listAll :[any ChatBaseView] = []
        var list = try await BackupsManager.shared.getDelegateDiary()?.queryChat(
            aiUserId: self.aiRole?.objectId ?? "",
            userId: BackupsManager.shared.backupsType == BackupsType.cloud ? userManager.getUserId() : "",
            isRefresh: isRefresh,
            timestamp: timestamp,
            pageSize: self.pageSize
        ) ?? []
        list = list.reversed()//倒序,因为数据库是倒序的，但是我们显示要最后一条是最新数据，所以在倒序一次
        for (index,bean) in list.enumerated() {
            bean.read = true
            //处理一些聊天内容更新
            try await AiChatConvertExt.dealChat(bean)
            //添加时间
            let previous : ChatBean? = {
                if index > 0, index <= list.count {
                    return list[index - 1]
                } else {
                    return nil
                }
            }()
            //添加时间
            let timeBean = self.addChatBean(previous: previous, time: bean.time)
            if timeBean != nil {
                listAll.append(timeBean!)
            }
            //添加聊天
            listAll.append(bean)
        }
        //更新数据，主要是已读
        try await BackupsManager.shared.getDelegateDiary()?.saveChat(list: list)
        return listAll
    }
    
    
    func getLast() -> ChatBean? {
        return items.last(where: { $0 is ChatBean}) as? ChatBean
    }
    
    //添加时间
    func addChatBean(previous: ChatBean?, time: Int64) -> ChatTimeBean? {
        if (previous == nil || TimeUtils.getDiffMinute(previous!.time, time) > diffMinuteValue) {
            let bean = ChatTimeBean()
            bean.time = time
            return bean
        }
        return nil
    }
    
    
    func doAddChat(text: String?, lastChatBean: ChatBean?, target: ChatBean? = nil, isEnd: Bool = false) {
        request(isLoading: false) {
            try check(!(text ?? "").isEmpty) {
                "聊天内容不能为空"
            }
            if isEnd && lastChatBean?.type == IChatType.END.rawValue{
                try check(false) {
                    "已经结束话题了，无需用在结束"
                }
            }
            let bean = ChatBean()
            bean.type = isEnd ? IChatType.END.rawValue : IChatType.USER.rawValue
            bean.contentType = target != nil ? IChatContentType.TYPE_CONTENT_TARGET.rawValue : IChatContentType.TYPE_CONTENT.rawValue
            bean.role = RoleBean.get(userManager.user,self.aiRole)
            bean.content = text
            bean.time = getMillisecond(try await getNetTime())
            bean.aiUserId = self.aiRole?.objectId
            bean.userId = BackupsManager.shared.backupsType == BackupsType.cloud ? userManager.getUserId() : ""
            bean.read = true
            
            if let target = target {
                let content = target.content
                let targetId = target.objectId
                var quote = ChatMessageBean.Quote()
                quote.target_id = targetId //引用id
                quote.target_sender = userManager.getName()
                quote.target_content = nil //需要根据targetId获取
                quote.reply_content = content
                bean.extra = toJson(quote)
            }
            
            let resultBean = try await BackupsManager.shared.getDelegateDiary()?.addChat(bean: bean)
            var listAll : [any ChatBaseView] = []
            //处理一一些聊天内容更新
            try await AiChatConvertExt.dealChat(bean)
            //添加时间
            let timeBean = self.addChatBean(previous: lastChatBean, time: bean.time)
            if timeBean != nil {
                listAll.append(timeBean!)
            }
            //添加聊天
            listAll.append(resultBean!)
            return listAll
        } suc: { list in
            self.targetChatBean = nil
            self.items.append(contentsOf: list)
            self.scrollBottom2 = true
            ActiveAiExt.checkChat() //检查聊天
        } fail: {
            showToast($0.message)
        }
    }
    
    private func addChat(contentType: Int,str: String, extra: String? = nil) async throws -> ChatBean {
        let newStr = EmojiUtils.deleteNoEmotionContent2(text: str) ?? ""
        let bean = ChatBean()
        bean.type = IChatType.USER.rawValue
        bean.contentType = contentType
        bean.role = RoleBean.get(userManager.user,self.aiRole)
        bean.content = newStr
        bean.time = getMillisecond(try await getNetTime())
        bean.aiUserId = self.aiRole?.objectId
        bean.userId = BackupsManager.shared.backupsType == BackupsType.cloud ? userManager.getUserId() : ""
        bean.read = true
        bean.extra = extra
        _ = try await BackupsManager.shared.getDelegateDiary()?.addChat(bean: bean)
        //处理一一些聊天内容更新
        try await AiChatConvertExt.dealChat(bean)
        return bean
    }
    
    func doAddPat(mAction: String?, suffixStr: String?) {
        guard let botRoleBean = aiRole else {
            return
        }
        
        request {
            try check(!(mAction ?? "").isEmpty) {
                "执行动作不能为空"
            }
            
            var pat = ChatMessageBean.Pat()
            pat.userId = botRoleBean.objectId
            pat.userName = botRoleBean.name
            pat.roleType = IRoleType.BOT.rawValue
            pat.action = mAction
            pat.suffix = suffixStr
            
            let bean = try await self.addChat(contentType: IChatContentType.TYPE_ACTION.rawValue, str: "",extra: toJson(pat))
            return bean
        } suc: { bean in
            self.items.append(bean)
            self.scrollBottom2 = true
            ActiveAiExt.checkChat() //检查聊天
        } fail: {
            showToast($0.message)
        }
    }
    
    func doAddChatPicture(emojiBean: EmojiPictureBean, lastChatBean: ChatBean?) {
        request(isLoading: true) {
            if (emojiBean.text ?? "")?.isEmpty == true {
                //如果图片信息为空，则开始ai读取
                emojiBean.text = try await ImageAiExt.instance.doAIImage(path: emojiBean.path ?? "", content: "")
                try check((emojiBean.text ?? "")?.isEmpty == false, "图片信息为空")
            }
            let bean = ChatBean()
            bean.type = IChatType.USER.rawValue
            bean.contentType = emojiBean.type == IEmojiType.TYPE_PICTURE.rawValue ? IChatContentType.TYPE_PICTURE.rawValue : IChatContentType.TYPE_EMOJI_PICTURE.rawValue
            bean.role = RoleBean.get(userManager.user,self.aiRole)
            bean.content = emojiBean.text
            bean.time = getMillisecond(try await getNetTime())
            bean.aiUserId = self.aiRole?.objectId
            bean.userId = BackupsManager.shared.backupsType == BackupsType.cloud ? userManager.getUserId() : ""
            bean.extra = toJson(emojiBean)
            bean.read = true
            
            let resultBean = try await BackupsManager.shared.getDelegateDiary()?.addChat(bean: bean)
            var listAll : [any ChatBaseView] = []
            //添加时间
            let timeBean = self.addChatBean(previous: lastChatBean, time: bean.time)
            if timeBean != nil {
                listAll.append(timeBean!)
            }
            //添加聊天
            listAll.append(resultBean!)
            return listAll
        } suc: { list in
            self.items.append(contentsOf: list)
            self.scrollBottom2 = true
            ActiveAiExt.checkChat() //检查聊天
        } fail: {
            showToast($0.message)
        }
    }
    
    func saveChat(bean: ChatBean,isToast: Bool = true) {
        request(isLoading: false) {
            try await BackupsManager.shared.getDelegateDiary()?.saveChat(bean: bean)
            return true
        } suc: { _ in
            if isToast {
                showToast("修改成功")
            }
        } fail: {
            showToast($0.message)
        }
    }
    
    func delChat(bean: ChatBean) {
        request(isLoading: false) {
            try await BackupsManager.shared.getDelegateDiary()?.deleteChat(bean: bean)
            let index = self.items.firstIndex(where: { $0.objectId == bean.objectId}) ?? -1
            return index
        } suc: { index in
            if index >= 0 {
                withAnimation {
                    self.items.remove(at: index)
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                    ActiveAiExt.checkChat() //检查聊天
                }
            }
        } fail: {
            showToast($0.message)
        }
    }
    
    //删除已经选中的
    func delChat() {
        request(isLoading: false) {
            let list = self.items
                .compactMap { $0 as? ChatBean } // 先筛选出 ChatBean 类型
                .filter { $0.select == true } // 再过滤 select 为 true 的
            try await BackupsManager.shared.getDelegateDiary()?.deleteChatList(beans: list)
            return true
        } suc: { _ in
            self.doReset()
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                ActiveAiExt.checkChat() //检查聊天
            }
            self.cancelChooseMode()
        } fail: {
            showToast($0.message)
        }
    }
    
    func clearChat() {
        request(isLoading: false) {
            let userId = BackupsManager.shared.backupsType == BackupsType.cloud ? userManager.getUserId() : ""
            try await BackupsManager.shared.getDelegateDiary()?.clearChat(aiUserId: self.aiRole?.objectId ?? "", userId: userId)
            return true
        } suc: { _ in
            self.items.removeAll()
        } fail: {
            showToast($0.message)
        }
    }
    
    func getAiRoleList() {
        self.aiItems.removeAll()
        request(isLoading: false) {
            let list = try await BackupsManager.shared.getDelegateConfig()?.getAiRoleList(false) ?? []
            list.forEach {
                $0.select = $0.objectId == self.aiRole?.objectId ?? ""
            }
            return list
        } suc: { list in
            self.aiItems.append(contentsOf: list)
        } fail: {
            showToast($0.message)
        }
    }
    
    func doVoice(bean: ChatBean, isStarted: Binding<Bool>) {
        if (bean.content ?? "").isEmpty {
            return
        }
        if AudioPlayerManager.shared.isPlaying() {
            showToast("正在播放，请稍后")
            return
        }
        var deduct = false
        request {
            try check(AiChatExt.isAiEnabled(2)) {
                "AI 功能已用尽,请前往活动领取次数"
            }
            
            let voiceId = bean.role?.voiceId
            try check((voiceId ?? "").isEmpty == false) {
                "voiceId 为空"
            }
            var audioData: Data? = nil
            if let path = bean.voicePath {
                showLog("播放文件已存在，直接播放")
                audioData = AudioFileManager.loadAudioData(path)
            }
            if audioData == nil {
                showLog("开始下载播放文件")
                let (_, hexData) = try await AiChatExt.doAiVoiceTest(text: bean.content, voiceId: voiceId, voiceHex: "")
                let key = "\(userManager.getUserId())_\(UUID())"
                _ = AudioFileManager.saveAudio(hexData!, key)
                bean.voicePath = key
                try await retry(maxRetries: 5, retryDelayMillis: 300) {
                    try await BackupsManager.shared.getDelegateDiary()?.saveChat(bean: bean)
                }
                audioData = hexData
                deduct = true
            }
            try check(audioData != nil, "播放失败")
            return audioData!
        } suc: { hexData in
            self.voiceStatus+=1 //更新语音状态
            isStarted.wrappedValue = true
            AudioPlayerManager.shared.playData(from: hexData)
            AudioPlayerManager.shared.onPlaybackFinished = {
                isStarted.wrappedValue = false
            }
            if deduct {
                AiChatExt.deductAiChatCount(2)
            }
        } fail: {
            showToast($0.message)
        }
        
    }
    
    //添加图片
    func doAddPicture(image: UIImage,text: String) {
        request(isLoading: true) {
            let key = UUID().uuidString
            let imageBean = try await LocalConfigImpl().upload(image: image, name: key)
            let bean = EmojiPictureBean()
            bean.path = imageBean.url
            bean.text = text
            bean.width = imageBean.width
            bean.height = imageBean.height
            bean.duration = imageBean.duration
            bean.mimeType = imageBean.mimeType
            bean.sort = 0
            bean.type = IEmojiType.TYPE_PICTURE.rawValue
            return bean
        } suc: { bean in
            //2个加载框很接近，容易导致失效，所以延迟
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.doAddChatPicture(emojiBean: bean, lastChatBean: self.getLast())
            }
        } fail: {
            showToast($0.message)
        }
    }
    
    //多选模式
    func setChooseMode() {
        withAnimation {
            choose = true
        }
    }
    
    //取消多选模式
    func cancelChooseMode() {
        withAnimation {
            choose = false
        }
    }
}
